# -*- coding: utf-8 -*-

# Copyright(c) 2011 OpenDomo Services SL. Licensed under GPL v3 or later
# Isidro Catalan <icatalan@opendomo.com>



"""
Provide access to resources served by Opendomo over HTTP.
"""

from elisa.core.components.resource_provider import ResourceProvider
from elisa.core.resource_manager import NoMatchingResourceProvider
from elisa.core.media_uri import MediaUri
from elisa.core.utils import defer
from elisa.core.utils.i18n import install_translation


from elisa.plugins.http_client.http_client import ElisaHttpClient
from elisa.plugins.http_client.extern.client_http import ClientRequest
from twisted.web2 import responsecode
from twisted.web2.stream import BufferedStream, MemoryStream, FileStream

from twisted.web.client import getPage
from twisted.internet import threads
from twisted.internet import reactor
from twisted.python import log
from elisa.core.utils.cancellable_defer import CancellableDeferred, \
                                               CancelledError

from elisa.plugins.base.models.media import RawDataModel
from elisa.plugins.base.models.image import ImageModel

from elisa.plugins.opendomo.settings import OpendomoLoginController

from elisa.plugins.opendomo import opendomo_api as opendomo
from elisa.plugins.opendomo.models import OpendomoResponseModel, LoginModel, ItemModel, \
                                          CommandModel, OpendomoPhotoModel

from elisa.plugins.poblesec.login import LoginController
import httplib
import re
import sys
import mimetools
import os.path
import time
from xml.dom import minidom

from twill import commands as twill

_ = install_translation('opendomo')
_p = install_translation('poblesec')

class OpendomoResourceProvider(ResourceProvider):

    """
    A resource provider that implements the GET and POST methods for use on the
    Opendomo API (see http://www.opendomo.com/services/api/ for details).
    The GET method is also able to retrieve image files from Opendomo directly.
    """

    # Queries to the Opendomo API
    api_uri = opendomo.BASE_API_URI
    api_re = re.compile(api_uri)
    supported_uri = api_uri
    logged_out = 0

    def initialize(self):
        dfr = super(OpendomoResourceProvider, self).initialize()

        def parent_initialized(result):
            self._api_client = \
                ElisaHttpClient(host=opendomo.AGENT_IP, pipeline=False)
            self.expired = True
            self.login_dfr = None
            self.authenticated = False
                     
            # Check the token validity
            def valid_token():
                self.authenticated = True

            def invalid_token():
                msg = 'Invalid authentication token. Please log in again.'
                self.warning(msg)
                # The user will have to log in again from the UI
                try:
                    os.unlink(opendomo.COOKIE_JAR)
                except OSError:
                    pass
                self.authenticated = False
            if opendomo.check_token() == True: 
               valid_token()
            else:
               
               invalid_token()
            return
           
        dfr.addCallback(parent_initialized)
        dfr.addCallback(lambda result: self)

        return dfr

    def clean(self):
        """
        Close all the open HTTP connections.
        """
        close_dfrs = []
        # Close the API connection
        close_dfrs.append(self._api_client.close())

        if self.login_dfr is not None:
            close_dfrs.append(self.login_dfr)

        dfr = defer.DeferredList(close_dfrs, consumeErrors=True)

        def parent_clean(result):
            return super(OpendomoResourceProvider, self).clean()

        dfr.addCallback(parent_clean)
        return dfr

    def login(self, username, password):
        """
        Login to the Opendomo webservice using the given credentials.

        @param username: username to request the login with
        @type username:  C{str}
        @param password: password to request the login with
        @type password:  C{str}

        @return: a deferred fired when successfully logged in
        @rtype:  L{elisa.core.utils.defer.Deferred}
        """
        login_model = LoginModel()
        login_model.username = username
        login_model.password = password

        def parse_login_web_form(login_url, username, password):
            # Clear previously cached Opendomo credentials
            twill.clear_cookies()
            print '----LOGIN-------'
            print login_url
            print username
            print password
            print '-----------'
            # This will take us to the login page
            twill.go(login_url)
    
            # We are now on the login page
            # Fill in credentials and submit
            twill.formvalue(1, 'USER', username)
            twill.formvalue(1, 'PASS', password)
            twill.submit()
            twill.save_cookies(opendomo.COOKIE_JAR)

    
        def auth_open_url(state, model):
             dfr = threads.deferToThread(parse_login_web_form, state['url'],
                                         username, password)
             return dfr

        dfr = opendomo.get_login_url(self)
        dfr.addCallback(auth_open_url, login_model)

        return dfr
        
    def logout(self):
        """
        Remove any reference to the current Opendomo authentication
        token and remove the file storing the Opendomo frob.
        """
        self.debug("Logging out.")
        self.authenticated = False
        try:
            os.unlink(opendomo.COOKIE_JAR)
        except OSError:
            pass
        self.debug("Removed cached cookie file: %s", opendomo.COOKIE_JAR)

    def is_logged_in(self):
        return opendomo.check_token()
        
    def get(self, uri, context_model=None):
        """
        Download an installer and store its data in a L{RawDataModel}.
        L{context_model} parameter is not used.
        """
      
        def response_read(response):
            # Parse the response and populate the model accordingly
            dom = minidom.parseString(response)
            rsp = dom.firstChild
            model=OpendomoResponseModel()
            if rsp.nodeName != 'odcgi':
                # Malformed response
                print "---Malformed response----"
                raise ValueError('There is no content to show')
            print '---DEGUG - RESPONSE---'
            print response
            print '-------END DEBUG------'
            model.gui_elements = []
            model.command_elements = []
            
            if rsp.getElementsByTagName('error'):
                # Process errors first!
                errorlist = rsp.getElementsByTagName('error')
                for error in errorlist:
                    description = error.attributes['description'].nodeValue
                    raise ValueError(description)
            
            if rsp.getElementsByTagName('gui')[0].getElementsByTagName('item'):
                # Process the menu item elements
                itemlist = rsp.getElementsByTagName('gui')[0].getElementsByTagName('item')
                for item in itemlist:
                    item_model = ItemModel()
                    item_model.label = item.attributes['label'].nodeValue
                    item_model.href = item.attributes['href'].nodeValue
                    model.gui_elements.append(item_model)       
               # Processing images
            if rsp.getElementsByTagName('gui')[0].getElementsByTagName('img'):
                # Process the menu item elements
                itemlist = rsp.getElementsByTagName('gui')[0].getElementsByTagName('img')
                for item in itemlist:
                    item_model = ItemModel()
                    item_model.label = item.attributes['alt'].nodeValue
                    item_model.href = item.attributes['src'].nodeValue
                    model.gui_elements.append(item_model)                        
                # Process the command items              
            elif rsp.getElementsByTagName('gui')[0].getElementsByTagName('commands'):
                 commandslist = rsp.getElementsByTagName('gui')[0].getElementsByTagName('commands')   
                 for commands in commandslist:
                     commandlist = commands.getElementsByTagName('command')
                     for command in commandlist:
                         command_model = CommandModel()
                         command_model.id = commands.attributes["id"].nodeValue
                         command_model.label = command.attributes['label'].nodeValue
                         command_model.url = command.attributes['url'].nodeValue
                         command_model.state = command.attributes['state'].nodeValue                    
                         model.command_elements.append(command_model)  
            elif rsp.getElementsByTagName('gui')[0].getElementsByTagName('password'):
                 self.logged_out = 1
            else:
                # Another type of response, default to filling model.data
                # with the raw XML response
                model.data = response

            return model
     
        def _cancel_request(deferred):
            deferred.errback(CancelledError('Cancelled request'))

        def connection_failed(deferred, model):
            return model
            
        def request_done(response, model):
            if response.code == responsecode.OK:
                return model
            elif response.code == responsecode.NOT_FOUND:
                # 404 error code: resource not found
                return defer.fail(IOError('Resource not found at %s' % url))
            else:
                # Other HTTP response code
#                return defer.fail(Exception('Received an %d HTTP response code' % response.code))
                # Other HTTP response code
                print '----RESPONSECODE------'
                print response.code
                return model
                        

        auth_str=opendomo.get_cached_token()
        if len(auth_str) > 0 and self.logged_out == 0:
              cookies_={'Cookie': auth_str}
              print 'Access with cookie'
        else:
           # We log in with the information provided in the file
           if len(opendomo.USER) and len(opendomo.PWD):
              print ' HACIENDO LOGIN!'
              od_login = OpendomoLoginController()
              dfr = od_login.login(opendomo.USER, opendomo.PWD)
              #FIXME: insincere workaround
              time.sleep(4)              
              # Checking the login again              
              auth_str = opendomo.get_cached_token()              
              if len(auth_str) > 0:
                 cookies_={'Cookie': auth_str}
                 self.logged_out = 0
              else:
                 raise ValueError('You need to login in order to access OpenDomo')

        """ TODO: Update Cookies for every request! """	        
        headers = { "Accept":"*/*", 'Cookie': auth_str}

        if isinstance(uri, MediaUri):
           h1 = httplib.HTTPConnection(uri.host)
        else:
           h1 = httplib.HTTPConnection(opendomo.AGENT_IP)
                
        h1.connect()
        if isinstance(uri, MediaUri):
           h1.request("GET", url=uri.path+'?'+uri.get_params_string(), headers=headers)
        else:
           h1.request("GET", url=uri, headers=headers)

        rsp = h1.getresponse()
        response=rsp.read()
        defer = CancellableDeferred(canceller=_cancel_request)

        if isinstance(uri, MediaUri):
           # FIXME: workaround to use the cgi response when processing 
           #        an image, the request is done twice

           model=OpendomoResponseModel()
           model.data = response      
           model.size = len(response)           
           http_client = ElisaHttpClient(host=opendomo.AGENT_IP, pipeline=False)           
           defer = http_client.request(uri.path+'?'+uri.get_params_string())
           defer.addCallback(request_done, model)           
           defer.addErrback(connection_failed, model)           
        else:
           model=response_read(response)

        return (model, defer)
        
    def getEntries(self, uri):
        model,defer = self.get(uri)
        entries=[]
        
        for item_model in model.gui_elements:
            if item_model.href.endswith("browseFiles.sh"):
               #Hack to avoid <error description="Invalid path XML"/> error 
               # when calling the script browseFiles.sh?GUI=XML
               item_model.href=item_model.href+'?option[]=/'
            elif item_model.href.find("browseFiles.sh") != -1:
               item_model.href='/cgi-bin/od.cgi/tools/'+item_model.href
            elif item_model.href.startswith("browseCaptures.sh"):
               item_model.href='/cgi-bin/od.cgi/control/'+item_model.href
               
            element = [_(item_model.label), True, item_model.href, opendomo.MENU_ITEM]
            entries.append(element)
        
        for command_model in model.command_elements:
            if command_model.state == 'off':
               element = [_(command_model.id + ' ' + command_model.label), 
                         True, opendomo.URL_ROOT+'/control/'+command_model.url, 
                         opendomo.COMMAND]
               entries.append(element)
        
        return entries

